Skip to content

Blocked BitPacked: decode blocked bitwidths - #10202

Merged
mhk197 merged 12 commits into
developfrom
mk/bitpacked-variable-decode
Oct 12, 2026
Merged

mhk197 merged 12 commits into
developfrom
mk/bitpacked-variable-decode

Conversation

@mhk197

@mhk197 mhk197 commented Oct 2, 2026 •

Copy link
Copy Markdown
Contributor

Tracking Issue: #10167

Summary

Adds support for decoding BitPacked arrays with blocked bitwidths.

Now each public function that decodes bitpacked arrays with global bitwidths has a version for blocked bitwidths.

Global width Blocked
unpack_array(array, ctx) unpack_array_blocked(array, offsets, ctx)
unpack_primitive_array unpack_primitive_array_blocked
unpack_into_primitive_builder unpack_into_primitive_builder_blocked
unpack_single(array, index) unpack_single_blocked(array, offsets, index, ctx)

Callers choose between them by matching on bit_widths().

There is no blocked unpack_map_into_builder: its only caller, the widening CastKernel, declines blocked arrays, so the mapped path would only run in tests.

Design

#10004 only checks block boundary values in debug builds, so the decoder checks them itself:

  • Bulk decode casts the offsets child to u64 and executes it once, checks that it holds one boundary per block plus a trailing one, and validates every boundary before writing to the builder. A single offset type keeps one copy of the block loop per value type, for 8 bytes per 1024 values. Each block must be a whole number of 128-byte rows, no wider than the array's type, and the boundaries must span the packed buffer.
  • scalar_at reads only the selected block's boundaries, directly for host primitive offsets and through execute_scalar otherwise, and validates just that block.

Both use block_range in bitpacking/array/mod.rs, which validate_primitive_offsets now also uses. Boundaries are relative to the first one, which may be nonzero.

Full blocks unpack straight into the output, like the global decoder's full chunks. A partial first or last block unpacks into one 1024-value scratch block.

@mhk197 mhk197 added the changelog/skip Do not list PR in the changelog label Oct 2, 2026
@mhk197 mhk197 changed the title Blocked BitPacked: decode variable bit widths Blocked BitPacked: decode blocked bit widths Oct 2, 2026
@mhk197
mhk197 force-pushed the mk/bitpacked-variable-decode branch from 6ada699 to e780ee8 Compare October 2, 2026 19:26
@codspeed

codspeed Bot commented Oct 2, 2026 •

Copy link
Copy Markdown

Merging this PR will regress 1 benchmark

⚠️ Unknown Walltime execution environment detected

Using the Walltime instrument on standard Hosted Runners will lead to inconsistent data.

For the most accurate results, we recommend using CodSpeed Macro Runners: bare-metal machines fine-tuned for performance measurement consistency.

⚠️ 17 benchmarks spent significant time in system calls

System calls cannot be consistently instrumented, so they are not included in the measure, which understates the real cost. Please switch to the Walltime instrument to accurately measure system calls.

Measurement and system calls

⚡ 6 improved benchmarks
❌ 1 regressed benchmark
✅ 2200 untouched benchmarks
🆕 6 new benchmarks

Warning

Please fix the performance issues or acknowledge them on CodSpeed.

Performance Changes

Mode Benchmark BASE HEAD Efficiency
❌ WallTime bitpack_blocked_compress_avx512 4.9 µs 5.6 µs -13.45%
⚡ WallTime threshold_neon[8] 4 µs 2.9 µs +37.2%
⚡ WallTime threshold_neon[80] 5.8 µs 4.9 µs +18.51%
⚡ WallTime threshold_neon[64] 5.3 µs 4.5 µs +18.17%
⚡ WallTime filter_neon[64] 3.5 µs 3 µs +16.09%
⚡ WallTime bitpack_blocked_compress_avx2 7.6 µs 6.8 µs +12.16%
⚡ WallTime filter_neon[80] 3.5 µs 3.2 µs +10.24%
🆕 WallTime bitpack_blocked_decompress_avx512[u32] N/A 9.4 µs N/A
🆕 WallTime bitpack_blocked_decompress_avx512[u64] N/A 18.7 µs N/A
🆕 WallTime bitpack_blocked_decompress_avx2[u32] N/A 10.8 µs N/A
🆕 WallTime bitpack_blocked_decompress_avx2[u64] N/A 21.2 µs N/A
🆕 WallTime bitpack_blocked_decompress_neon[u32] N/A 13.9 µs N/A
🆕 WallTime bitpack_blocked_decompress_neon[u64] N/A 30 µs N/A

Tip

Investigate this regression by commenting @codspeedbot fix this regression on this PR, or directly use the CodSpeed MCP with your agent.


Comparing mk/bitpacked-variable-decode (d2a6f7e) with develop (9d5a844)

Open in CodSpeed

@mhk197
mhk197 force-pushed the mk/bitpacked-variable-decode branch 3 times, most recently from 957cefe to 67da367 Compare October 2, 2026 21:08
@mhk197
mhk197 changed the base branch from mk/bitpacked-stack-03-width-child to mk/bitpacked-variable-encode October 2, 2026 21:08
@mhk197
mhk197 added this pull request to stack #10243 October 2, 2026 21:14
@mhk197
mhk197 marked this pull request as ready for review October 2, 2026 21:35
@mhk197 mhk197 changed the title Blocked BitPacked: decode blocked bit widths Blocked BitPacked: decode blocked bitwidths Oct 2, 2026
@mhk197
mhk197 force-pushed the mk/bitpacked-variable-decode branch from b6f53eb to c70f089 Compare October 5, 2026 15:50
@mhk197
mhk197 force-pushed the mk/bitpacked-variable-decode branch 2 times, most recently from 760be2a to 656dea8 Compare October 5, 2026 16:39
Base automatically changed from mk/bitpacked-variable-encode to develop October 6, 2026 14:16
@joseph-isaacs
joseph-isaacs force-pushed the mk/bitpacked-variable-decode branch from 656dea8 to 0fdbb09 Compare October 6, 2026 14:16
@mhk197
mhk197 requested a review from joseph-isaacs October 9, 2026 15:04
Comment on lines +113 to +118

decode_blocks(array, &offsets, uninit_slice);

if let Some(patches) = array.patches() {
apply_patches_to_uninit_range(&mut uninit_range, &patches, ctx, |v: T| v)?;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you want overlap patches and blocks

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is what global does as well, can have as a followup pr

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sure but todo for now?

@mhk197
mhk197 requested a review from joseph-isaacs October 12, 2026 13:36
mhk197 added 12 commits October 12, 2026 09:43
Signed-off-by: Matt Katz <mhkatz97@gmail.com>
Signed-off-by: Matt Katz <mhkatz97@gmail.com>
Signed-off-by: Matt Katz <mhkatz97@gmail.com>
Signed-off-by: Matt Katz <mhkatz97@gmail.com>
Signed-off-by: Matt Katz <mhkatz97@gmail.com>
Signed-off-by: Matt Katz <mhkatz97@gmail.com>
Signed-off-by: Matt Katz <mhkatz97@gmail.com>
Signed-off-by: Matt Katz <mhkatz97@gmail.com>
Signed-off-by: Matt Katz <mhkatz97@gmail.com>
Signed-off-by: Matt Katz <mhkatz97@gmail.com>
Signed-off-by: Matt Katz <mhkatz97@gmail.com>
Signed-off-by: Matt Katz <mhkatz97@gmail.com>
@mhk197
mhk197 force-pushed the mk/bitpacked-variable-decode branch from 75fe6ef to d2a6f7e Compare October 12, 2026 13:45
@mhk197
mhk197 merged commit 583a985 into develop Oct 12, 2026
86 of 87 checks passed
@mhk197
mhk197 deleted the mk/bitpacked-variable-decode branch October 12, 2026 14:01
mhk197 added a commit that referenced this pull request Oct 12, 2026
Tracking Issue: #10167

## Summary

Adds a wire format for `BitPacked` arrays whose blocks each have their
own bit width, and makes it the in-memory ID, following
`fastlanes.for.v2` (#10135) and `DecimalByteParts`.

| ID | Role |
|---|---|
| `fastlanes.bitpacked` | Frozen wire format for global-width arrays.
Unchanged. |
| `fastlanes.bitpacked.v2` | In-memory ID, and the wire format for
per-block bit widths. |

`BitPackedPlugin` declares both serialized IDs and picks one per array
from `bit_widths()`:

- **Global width** writes `fastlanes.bitpacked` exactly as before.
Existing files read as before, and default writes stay byte-identical.
- **Blocked** writes `fastlanes.bitpacked.v2`.

The plugin moves into `bitpacking/plugin/`, laid out like FoR's:
`mod.rs` holds the IDs and the dispatch, `v1.rs` the v1 metadata and
serde, `v2.rs` the new format, and `tests.rs` the existing tests plus
new ones. The v1 code is moved unchanged, except that it writes
`bitpacked_v1_id()` rather than the plugin's ID.

## `fastlanes.bitpacked.v2` layout

- **Buffers**: the packed buffer, as in v1.
- **Children**: v1's children (patch indices, values and chunk offsets,
then validity), followed by the block offsets. The block offsets are
serialized like any other child, so the compressor can compress them.
- **Metadata**: a prost message holding `offset`, the block offsets'
unsigned `PType`, and the patches metadata. There is no bit width, and
the block offsets' length, `ceil((offset + len) / 1024) + 1`, is
derived.

Deserialization checks for one buffer and the expected number of
children, and validates the offset before deriving the block offsets'
length. Construction then checks the block offsets' dtype and length.
The decoder (#10202) bounds-checks the boundary values themselves.

## `BitPackedPatchedPlugin`

The experimental-patches plugin declares both IDs as well. It moves a v2
array's interior patches into a `Patched` wrapper just as it does for
v1, rebuilding the inner array with its block offsets instead of
panicking on a blocked array.

## Editions and compressor

`fastlanes.bitpacked.v2` is in no edition. Writers that enforce editions
reject it, and readers that predate it report an unknown encoding.
Nothing produces blocked arrays yet, so default writes don't change.
`BitPackingScheme::produced_encodings` now declares `bitpacked_v1_id()`,
since `BitPacked.id()` is the in-memory ID.

---------

Signed-off-by: Matt Katz <mhkatz97@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

changelog/skip Do not list PR in the changelog

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants